using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;

namespace LightCAD.MathLib
{
    public class SphericalHarmonics3
    {
        #region Properties



        public ListEx<Vector3> Coefficients;

        #endregion

        #region constructor
        public SphericalHarmonics3()
        {
            this.Coefficients = new ListEx<Vector3>();
            for (double i = 0; i < 9; i++)
            {
                this.Coefficients.Push(new Vector3());
            }
        }
        #endregion

        #region methods
        public SphericalHarmonics3 Set(Vector3[] coefficients)
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].Copy(coefficients[i]);
            }
            return this;
        }
        public SphericalHarmonics3 Zero()
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].Set(0, 0, 0);
            }
            return this;
        }
        public object GetAt(Vector3 normal, Vector3 target)
        {
            // normal is assumed to be unit length
            double x = normal.X, y = normal.Y, z = normal.Z;
            ListEx<Vector3> coeff = this.Coefficients;
            // band 0
            target.Copy(coeff[0]).MulScalar(0.282095);
            // band 1
            target.AddScaledVector(coeff[1], 0.488603 * y);
            target.AddScaledVector(coeff[2], 0.488603 * z);
            target.AddScaledVector(coeff[3], 0.488603 * x);
            // band 2
            target.AddScaledVector(coeff[4], 1.092548 * (x * y));
            target.AddScaledVector(coeff[5], 1.092548 * (y * z));
            target.AddScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0));
            target.AddScaledVector(coeff[7], 1.092548 * (x * z));
            target.AddScaledVector(coeff[8], 0.546274 * (x * x - y * y));
            return target;
        }
        public object GetIrradianceAt(Vector3 normal, Vector3 target)
        {
            // normal is assumed to be unit length
            double x = normal.X, y = normal.Y, z = normal.Z;
            ListEx<Vector3> coeff = this.Coefficients;
            // band 0
            target.Copy(coeff[0]).MulScalar(0.886227); // π * 0.282095
                                                            // band 1
            target.AddScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603
            target.AddScaledVector(coeff[2], 2.0 * 0.511664 * z);
            target.AddScaledVector(coeff[3], 2.0 * 0.511664 * x);
            // band 2
            target.AddScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548
            target.AddScaledVector(coeff[5], 2.0 * 0.429043 * y * z);
            target.AddScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3
            target.AddScaledVector(coeff[7], 2.0 * 0.429043 * x * z);
            target.AddScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274
            return target;
        }
        public SphericalHarmonics3 Add(SphericalHarmonics3 sh)
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].Add(sh.Coefficients[i]);
            }
            return this;
        } 
        public SphericalHarmonics3 AddScaledSH(SphericalHarmonics3 sh, double s)
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].AddScaledVector(sh.Coefficients[i], s);
            }
            return this;
        }
        public SphericalHarmonics3 Scale(double s)
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].MulScalar(s);
            }
            return this;
        }
        public SphericalHarmonics3 Lerp(SphericalHarmonics3 sh, double alpha)
        {
            for (int i = 0; i < 9; i++)
            {
                this.Coefficients[i].Lerp(sh.Coefficients[i], alpha);
            }
            return this;
        }
        public virtual bool Equals(SphericalHarmonics3 sh)
        {
            for (int i = 0; i < 9; i++)
            {
                if (!this.Coefficients[i].Equals(sh.Coefficients[i]))
                {
                    return false;
                }
            }
            return true;
        }
        public SphericalHarmonics3 Copy(SphericalHarmonics3 sh)
        {
            return this.Set(sh.Coefficients.ToArray());
        }
        public virtual SphericalHarmonics3 Clone()
        {
            return new SphericalHarmonics3().Copy(this);
        }
        public SphericalHarmonics3 FromArray(double[] array, int offset = 0)
        {
            ListEx<Vector3> coefficients = this.Coefficients;
            for (int i = 0; i < 9; i++)
            {
                coefficients[i].FromArray(array, offset + (i * 3));
            }
            return this;
        }
        public double[] ToArray(double[] array = null, int offset = 0)
        {
            if (array == null) array = new double[27];
            ListEx<Vector3> coefficients = this.Coefficients;
            for (int i = 0; i < 9; i++)
            {
                coefficients[i].ToArray(array, offset + (i * 3));
            }
            return array;
        }
        public static void GetBasisAt(Vector3 normal, double[] shBasis)
        {
            // normal is assumed to be unit length
            double x = normal.X, y = normal.Y, z = normal.Z;
            // band 0
            shBasis[0] = 0.282095;
            // band 1
            shBasis[1] = 0.488603 * y;
            shBasis[2] = 0.488603 * z;
            shBasis[3] = 0.488603 * x;
            // band 2
            shBasis[4] = 1.092548 * x * y;
            shBasis[5] = 1.092548 * y * z;
            shBasis[6] = 0.315392 * (3 * z * z - 1);
            shBasis[7] = 1.092548 * x * z;
            shBasis[8] = 0.546274 * (x * x - y * y);
        }
        #endregion

    }
}
